package com.fjnu.aspect;

import com.fjnu.annotation.PerformanceMonitor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class PerformanceMonitorAspect {

    private static final long DEFAULT_ALARM_TIME = 3000L;

    /**
     * 监控带有 @PerformanceMonitor 注解的方法
     */
    @Pointcut("@annotation(performanceMonitor)")
    public void annotatedMethods(PerformanceMonitor performanceMonitor) {}

    /**
     * 监控所有 select 和 update 方法
     */
    @Pointcut("execution(* com.fjnu.dao..*.select*(..)) || execution(* com.fjnu.dao..*.update*(..))")
    public void daoSelectAndUpdateMethods() {}

    /**
     * 方案1：分别处理带注解和不带注解的方法（推荐）
     */

    // 处理带 @PerformanceMonitor 注解的方法
    @Around("annotatedMethods(monitor)")
    public Object monitorAnnotatedMethods(ProceedingJoinPoint joinPoint,
                                          PerformanceMonitor monitor) throws Throwable {
        return monitorPerformance(joinPoint, monitor.alarmedTime());
    }

    // 处理不带注解的 DAO 方法
    @Around("daoSelectAndUpdateMethods() && !@annotation(com.fjnu.annotation.PerformanceMonitor)")
    public Object monitorDaoMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        return monitorPerformance(joinPoint, DEFAULT_ALARM_TIME);
    }

    /**
     * 方案2：使用统一的切点（备选）
     */
    // @Around("(annotatedMethods(monitor) && @annotation(monitor)) || " +
    //         "(daoSelectAndUpdateMethods() && !@annotation(com.fjnu.annotation.PerformanceMonitor))")
    // public Object monitorAllMethods(ProceedingJoinPoint joinPoint, PerformanceMonitor monitor) throws Throwable {
    //     long alarmTime = (monitor != null) ? monitor.alarmedTime() : DEFAULT_ALARM_TIME;
    //     return monitorPerformance(joinPoint, alarmTime);
    // }

    /**
     * 统一的性能监控逻辑
     */
    private Object monitorPerformance(ProceedingJoinPoint joinPoint, long alarmedTime) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();

        log.info("[PerformanceMonitor] {}.{} started", className, methodName);

        try {
            Object result = joinPoint.proceed();

            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;

            log.info("[PerformanceMonitor] {}.{} completed in {} ms", className, methodName, executionTime);

            if (executionTime > alarmedTime) {
                log.warn("[PerformanceMonitor] ⚠️ {}.{} takes {} ms, exceeds {} ms threshold",
                        className, methodName, executionTime, alarmedTime);
            }

            return result;
        } catch (Throwable throwable) {
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;

            log.error("[PerformanceMonitor] {}.{} failed after {} ms with error: {}",
                    className, methodName, executionTime, throwable.getMessage());
            throw throwable;
        }
    }
}